package com.whitehot.klib.crash;

import android.app.Application;
import android.content.Context;
import android.content.Intent;
import android.os.Build;
import android.os.Process;
import android.text.TextUtils;

import com.whitehot.klib.utils.FileUtils;
import com.whitehot.klib.utils.SystemUtils;
import com.whitehot.klib.utils.TimeUtils;

import java.io.PrintWriter;
import java.io.StringWriter;
import java.io.Writer;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;

import ndkmaster.NativeX;

/**
 * Created by ytf on 2016/12/7.
 * descption: 全局异常处理类
 */

public class CrashHandler implements Thread.UncaughtExceptionHandler
{
    private Application application;
    private Thread.UncaughtExceptionHandler defaultHandler;
    private MyDbHelper dbHelper;
    private static CrashHandler instance;
    private String uploadPath;

    public static CrashHandler getInstance()
    {
        if(instance == null)
        {
            synchronized(CrashHandler.class)
            {
                if(instance == null)
                {
                    instance = new CrashHandler();
                }
            }
        }
        return instance;
    }

    public MyDbHelper getDbHelper()
    {
        return dbHelper;
    }

    public String getUploadPath()
    {
        return uploadPath;
    }

    private CrashHandler()
    {
    }

    /***
     * 在application里面使用CarshHandler.init(this)初始化
     * @param context
     */
    public void init(Application context, String uploadPath)
    {
        init(context, null, uploadPath);
    }

    /***
     * 在application里面使用CarshHandler.init(this)初始化
     * @param context
     */
    public void init(Application context, OnShutdownListener l, String uploadPath)
    {
        this.uploadPath = uploadPath;
        Thread.setDefaultUncaughtExceptionHandler(this);
        this.application = context;
        defaultHandler = Thread.getDefaultUncaughtExceptionHandler();
        ll = l;
        dbHelper = new MyDbHelper(application);
        application.startService(new Intent(application, UploadCrashLogService.class));
    }

    @Override
    public void uncaughtException(Thread thread, Throwable ex)
    {
        if(ll != null)
        {
            ll.shutdown();
        }

        if (isHandler(ex))
        {
            collectDeviceInfo(application);
            handleException(ex);
        } else
        {
            defaultHandler.uncaughtException(thread, ex);
        }
    }

    private void handleException(Throwable ex)
    {
        String logPath = saveCrashInfo2File(ex);
        if(logPath != null)
        {
            saveRecord(logPath);
            application.startService(new Intent(application, UploadCrashLogService.class));
        }
        Process.killProcess(Process.myPid());
        System.exit(0);
    }

    private void saveRecord(String logPath)
    {
        CrashBean cb = new CrashBean();
        cb.crash_time = System.currentTimeMillis();
        cb.isUploaded = 0;
        cb.logFilePath = logPath;
        dbHelper.insert(cb);
    }

    //用来存储设备信息和异常信息
    private Map<String, String> infos = new HashMap<String, String>();

    /**
     * 收集设备参数信息
     *
     * @param ctx
     */
    public void collectDeviceInfo(Context ctx)
    {
        infos.clear();
        Field[] fields = Build.class.getDeclaredFields();
        for (Field field : fields)
        {
            try
            {
                field.setAccessible(true);
                infos.put(field.getName(), field.get(null).toString());
            } catch (Exception e)
            {
            }
        }
        infos.put("Android Platform", String.valueOf(Build.VERSION.SDK_INT));
        infos.put("AppVersion", "App Version->" + SystemUtils.getVersionName(ctx));
    }

    /**
     * 保存错误信息到文件中
     *
     * @param ex
     * @return 返回文件名称, 便于将文件传送到服务器
     */
    private String saveCrashInfo2File(Throwable ex)
    {
        StringBuffer sb = new StringBuffer();
        sb.append("Device Info: \n");
        for (Map.Entry<String, String> entry : infos.entrySet())
        {
            String key = entry.getKey();
            String value = entry.getValue();
            sb.append(key + ":  " + value + "\n");
        }
        sb.append("\nError Info: \n");
        Writer writer = new StringWriter();
        PrintWriter printWriter = new PrintWriter(writer);
        ex.printStackTrace(printWriter);
        Throwable cause = ex.getCause();
        while (cause != null)
        {
            cause.printStackTrace(printWriter);
            cause = cause.getCause();
        }
        printWriter.close();
        String result = writer.toString();
        sb.append(result);

        String fileName = TimeUtils.getNowMills() + ".log";
        String absPath = FileUtils.createExternalFile(application, "/log", fileName);
        if(!TextUtils.isEmpty(absPath))
        {
            int code = NativeX.writeString(absPath, sb.toString());
            if(code > 0)
            {
                return absPath;
            }else
            {
                return null;
            }
        }
        return null;
    }

    private boolean isHandler(Throwable ex)
    {
        // 排序不需要捕获的情况
        if (ex == null)
        {
            return false;
        }
        return true;
    }

    private OnShutdownListener ll;

    public void setOnShutdownListener(OnShutdownListener l)
    {
        ll = l;
    }

    /***
     * 用于在app出现异常时关闭一些资源，比如友盟统计
     */
    public interface OnShutdownListener
    {
        void shutdown();
    }
}
